package com.glsc.ngateway.oaflow.config;

import com.glsc.ngateway.common.base.enums.RabbitExcEnum;
import com.glsc.ngateway.common.base.enums.RabbitQEnum;
import org.springframework.amqp.core.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class RabbitQueueRegister implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private BeanDefinitionRegistry beanDefinitionRegistry;


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        this.beanDefinitionRegistry = beanDefinitionRegistry;

        String bindingSuffix = "Binding";
        for (RabbitExcEnum exc : RabbitExcEnum.values()) {
            beanDefinitionRegistry.registerBeanDefinition(exc.getBeanName(),
                    exc.isFanoutExchange() ?
                            BeanDefinitionBuilder.genericBeanDefinition(FanoutExchange.class, () -> ExchangeBuilder.fanoutExchange(exc.getExchange())
                                    .durable(true).build()).getBeanDefinition() :
                            BeanDefinitionBuilder.genericBeanDefinition(DirectExchange.class, () -> ExchangeBuilder.directExchange(exc.getExchange())
                                    .durable(true).build()).getBeanDefinition()
            );
        }

        for (RabbitQEnum q : RabbitQEnum.values()) {
            beanDefinitionRegistry.registerBeanDefinition(q.getBeanName(),
                    BeanDefinitionBuilder.genericBeanDefinition(Queue.class,
                                    () -> {
                                        Queue queue;
                                        if (q.getForwardQueue() != null) {
                                            Map<String, Object> params = new HashMap<>();
                                            params.put("x-message-ttl", 60000);  // 60秒后过期
                                            //声明当前队列绑定的死信交换机
                                            params.put("x-dead-letter-exchange", q.getForwardQueue().getExchange().getExchange());
                                            //声明当前队列的死信路由键
                                            params.put("x-dead-letter-routing-key", q.getForwardQueue().getRoutingKey());
                                            queue = QueueBuilder.durable(q.getRoutingKey()).withArguments(params).build();

                                        } else {
                                            queue = new Queue(q.getRoutingKey());
                                        }

                                        return queue;
                                    })
                            .getBeanDefinition()
            );

            //注册队列与交换机的绑定
            beanDefinitionRegistry.registerBeanDefinition(q.getBeanName() + bindingSuffix,
                    BeanDefinitionBuilder.genericBeanDefinition(Binding.class,
                                    () -> {
                                        try {
                                            if (RabbitExcEnum.seekByBeanName(q.getExchange().getBeanName()).isFanoutExchange()) {
                                                return BindingBuilder.bind(applicationContext.getBean(q.getBeanName(), Queue.class))
                                                        .to(applicationContext.getBean(q.getExchange().getBeanName(), FanoutExchange.class));
                                            } else {
                                                return BindingBuilder.bind(applicationContext.getBean(q.getBeanName(), Queue.class))
                                                        .to(applicationContext.getBean(q.getExchange().getBeanName(), DirectExchange.class))
                                                        .with(q.getRoutingKey());
                                            }
                                        } catch (Exception e) {
                                            throw new RuntimeException(e);
                                        }
                                    })
                            .getBeanDefinition()
            );
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}
